function Selection(textView){
    this.$initEventable();
    this.$initRenderable();
    this.m_textView = textView;
    this.$on(this.m_textView, 'mousedown', this._onMouseDown);
    this.$on(this.m_textView.textModel(), 'textchanged', this._onTextChanged);
    this.m_focusOffset = 0;
    this.m_anchorOffset = 0;
}

Selection.prototype = {
    _TPL_ELM: '<span id="#{id}"></span>',
    startOffset: function (){
        return this.m_focusOffset < this.m_anchorOffset ?
            this.m_focusOffset : this.m_anchorOffset;
    },
    endOffset: function (){
        return this.m_focusOffset < this.m_anchorOffset ?
            this.m_anchorOffset : this.m_focusOffset;
    },
    focusOffset: function (){
        return this.m_focusOffset;
    },
    anchorOffset: function (){
        return this.m_anchorOffset;
    },
    isCollapsed: function (){
        return this.m_focusOffset == this.m_anchorOffset;
    },
    collapse: function (toStart){
        this.setOffset(toStart ? this.startOffset() : this.endOffset());
    },
    setOffset: function (offset){
        this.m_focusOffset = offset;
        this.m_anchorOffset = offset;
        this._selectionChange();
    },
    setFocusOffset: function (offset){
        this.m_focusOffset = offset;
        this._selectionChange();
    },
    setAnchorOffset: function (offset){
        this.m_anchorOffset = offset;
        this._selectionChange();
    },
    moveLeft: function (shift){
        var offset = shift ? this.anchorOffset() : this.focusOffset();
        offset = Math.max(0, offset - 1);
        if (shift) {
            this.setAnchorOffset(offset);
        } else {
            this.setOffset(offset);
        }
    },
    $renderHTML: function (){
        return Utils.format(this._TPL_ELM, { id: this.$id() });
    },
    _onMouseDown: function (evt){
        var hitTestInfo = this.m_textView.hitTest({
            x: evt.clientX,
            y: evt.clientY
        }, true);
        this.$on(document, 'mousemove', this._onMouseMove);
        this.$on(document, 'mouseup', this._onMouseUp);
        this.setOffset(hitTestInfo.offset);
    },
    _onMouseMove: function (evt){
        var hitTestInfo = this.m_textView.hitTest({
            x: evt.clientX,
            y: evt.clientY
        }, true);
        this.setFocusOffset(hitTestInfo.offset);
    },
    _onMouseUp: function (evt){
        var hitTestInfo = this.m_textView.hitTest({
            x: evt.clientX,
            y: evt.clientY
        }, true);
        this.$un(document, 'mousemove', this._onMouseMove);
        this.$un(document, 'mouseup', this._onMouseUp);
        this.setFocusOffset(hitTestInfo.offset);
    },
    _selectionChange: function (){
        this.dispatchEvent({
            type: 'selectionchange'
        });
        var textView = this.m_textView;
        var textModel = this.m_textView.textModel();
        var buff = [];
        // todo: 将高亮当前行移到插件中去处理. 需要判断焦点情况（和cursor类似）
        var focusLineIndex = textModel.getLineIndex(this.focusOffset());
        var focusPosition = textView.getPosition(this.focusOffset());
        buff.push('<div class="QEActiveLine" style="left:0;top:', focusPosition.y,
                'px;width:', textView.contentWidth(),
                'px;height:', this.m_textView.getLineHeight(focusLineIndex),
                'px"></div>');
        if (!this.isCollapsed()) {
            var startLineIndex = textModel.getLineIndex(this.startOffset());
            var endLineIndex = textModel.getLineIndex(this.endOffset());
            var startPosition = this.m_textView.getPosition(this.startOffset());
            var endPosition = this.m_textView.getPosition(this.endOffset());
            if (startLineIndex === endLineIndex) {
                buff.push('<div class="QESelectionPart" style="left:', startPosition.x,
                    'px;top:', startPosition.y,
                    'px;width:', endPosition.x - startPosition.x,
                    'px;height:', this.m_textView.getLineHeight(startLineIndex),
                    'px"></div>');
            } else {
                buff.push('<div class="QESelectionPart" style="left:', startPosition.x,
                    'px;top:', startPosition.y,
                    'px;width:', textView.contentWidth() - startPosition.x,
                    'px;height:', this.m_textView.getLineHeight(startLineIndex),
                    'px"></div>');
                buff.push('<div class="QESelectionPart" style="left:0;top:',
                    startPosition.y + this.m_textView.getLineHeight(startLineIndex),
                    'px;width:', this.m_textView.contentWidth(),
                    'px;height:', endPosition.y - startPosition.y - textView.getLineHeight(endLineIndex),
                    'px"></div>');
                buff.push('<div class="QESelectionPart" style="left:0;top:', endPosition.y,
                    'px;width:', endPosition.x,
                    'px;height:', this.m_textView.getLineHeight(startLineIndex),
                    'px"></div>');
            }
        }
        this.$elm().innerHTML = buff.join('');
    },
    _onTextChanged: function (evt){
        // todo: 用一个positionUpdater统一这些更新offset的事情.
        var offset = evt.offset;
        var length = evt.oldText.length;
        var newLength = evt.newText.length;
        if (this.m_focusOffset > offset) {
            if (this.m_focusOffset < offset + length) {
                this.m_focusOffset = offset;
            } else {
                this.m_focusOffset += newLength - length;
            }
        }
        if (this.m_anchorOffset > offset) {
            if (this.m_anchorOffset < offset + length) {
                this.m_anchorOffset = offset;
            } else {
                this.m_anchorOffset += newLength - length;
            }
        }
        this._selectionChange();
    }
};

Utils.extend(Selection, Eventable);
Utils.extend(Selection, Renderable);